package com.leemon.mall.admin.security;

import com.leemon.mall.common.util.RedisUtil;
import com.leemon.mall.security.constants.SecurityConstants;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

import java.util.Map;

/**
 * @author limenglong
 * @create 2019-09-03 14:52
 * @desc 自定义AuthenticationProvider 实现后台管理员账号密码登陆
 **/

@Component
public class AdminAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private SysUserDetailsService userDetailsService;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {

        String sessionUUID = null;
        String imageCode = null;

        if (authentication instanceof AdminAuthenticationToken) {
            AdminAuthenticationToken token = (AdminAuthenticationToken) authentication;
            sessionUUID = token.getSessionUUID();
            imageCode = token.getImageCode();
        }else{
            Map<String,String> parameterMap = (Map<String, String>) authentication.getDetails();
            sessionUUID = parameterMap.get("sessionUUID").toString();
            imageCode  = parameterMap.get("imageCode");
        }

        //检索用户之前，先校验验证码
        String kaptchaKey = SecurityConstants.SPRING_SECURITY_RESTFUL_IMAGE_CODE + sessionUUID;
        String kaptcha = redisUtil.get(kaptchaKey);
        //从Redis中读过数据之后立马删除缓存
        redisUtil.delete(kaptchaKey);
        if (StringUtils.isBlank(imageCode) || !imageCode.equals(kaptcha)) {
            throw new AuthenticationServiceException("验证码有误");
        }

        UserDetails userDetails = userDetailsService.loadUserByUsername(username);
        if (userDetails == null) {
            throw new InternalAuthenticationServiceException("账号或密码不正确");
        }

        //库中加密的密码
        String encodePassword = userDetails.getPassword();
        //用户登录输入的密码
        String rawPassowrd = authentication.getCredentials().toString();
        //校验密码
        if (!passwordEncoder.matches(rawPassowrd, encodePassword)) {
            throw new BadCredentialsException("账号或密码不正确");
        }

        if (!userDetails.isEnabled()) {
            throw new UsernameNotFoundException("账号已被锁定,请联系管理员");
        }

        return userDetails;
    }

    /**
     * 主要是用来检验userDetails的信息
     *
     * @param userDetails
     * @param authentication
     * @throws AuthenticationException
     */
    @Override
    protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        //除去用户名的其他参数校验，此处整合到了retrieveUser方法中
    }

}
